package de.chessgame.gamelogic.piece;

import java.util.ArrayList;

import de.chessgame.gamelogic.Board;
import de.chessgame.gamelogic.Coordinate;

public class King extends Piece {

	private final int BOARD_SIZE = 8;

	public King(Fraction fraction) {
		super(Type.KING, fraction);
	}

	@Override
	public void calculatePossibleMoves(Piece[][] content, int xOld, int yOld, boolean inCheck) {
		possibleMoves.clear();
		super.calculatePossibleMoves(content, xOld, yOld, inCheck);

		possibleMoves.addAll(givePossibleMovesForKing(content, xOld, yOld, inCheck));
	}

	@Override
	public boolean canMove(Piece[][] content, int xOld, int yOld, boolean inCheck) {
		return givePossibleMovesForKing(content, xOld, yOld, inCheck).size() != 0;
	};

	private ArrayList<Coordinate> givePossibleMovesForKing(Piece[][] content,
			int xOld, int yOld, boolean inCheck) {
		ArrayList<Coordinate> list = new ArrayList<Coordinate>();
		for (int xTry = xOld - 1; xTry <= xOld + 1; xTry++) {
			for (int yTry = yOld - 1; yTry <= yOld + 1; yTry++) {
				if ((xTry != xOld || yTry != yOld)
						&& isSquareOnBoard(content, xTry, yTry)
						&& (content[yTry][xTry] == null || content[yTry][xTry]
								.getFraction() != fraction)) {
					list.add(new Coordinate(xTry, yTry));
				}
			}
		}

		// left
		if (isCastlingPossible(content, xOld, yOld, true, inCheck)) {
			list.add(new Coordinate(xOld - 2, yOld));
		}

		// right
		if (isCastlingPossible(content, xOld, yOld, false, inCheck)) {
			list.add(new Coordinate(xOld + 2, yOld));
		}

		list = removeIrregularyMoves(content, list, xOld, yOld);

		return list;
	}

	private boolean isCastlingPossible(Piece[][] content, int xOld, int yOld,
			boolean inCheck, boolean left) {
		Piece rook = content[yOld][left ? 0 : BOARD_SIZE - 1];

		if (moveCount != 0 || inCheck || rook == null
				|| rook.getType() != Type.ROOK
				|| rook.getFraction() != fraction || rook.moveCount != 0) {
			return false;
		}

		int xDirection = left ? -1 : 1;
		Piece pieceCheck;
		for (int xTry = xOld + xDirection; (pieceCheck = content[yOld][xTry]) != rook; xTry += xDirection) {
			if (pieceCheck != null) {
				return false;
			}
		}
		return true;
	}

	@Override
	public boolean onMove(Piece[][] content, int xOld, int yOld, int x, int y) {
		super.onMove(content, xOld, yOld, x, y);
		int xMove = x - xOld;

		if (xMove > -2 && xMove < 2) {
			return false;
		}

		// Castling:
		boolean left = xMove < 0;
		if (left) {
			content[y][3] = content[y][0];
			content[y][3].onMove(content, 0, y, 3, y);
			content[y][0] = null;
		} else {
			content[y][BOARD_SIZE - 1 - 2] = content[y][BOARD_SIZE - 1];
			content[y][BOARD_SIZE - 1 - 2].onMove(content, BOARD_SIZE - 1 - 2,
					y, BOARD_SIZE - 1, y);
			content[y][BOARD_SIZE - 1] = null;
		}
		return true;
	}

	@Override
	public boolean onMoveDontChangePiece(Piece[][] content, int xOld, int yOld,
			int x, int y) {
		super.onMoveDontChangePiece(content, xOld, yOld, x, y);
		int xMove = x - xOld;

		if (xMove > -2 && xMove < 2) {
			return false;
		}

		// Castling:
		boolean left = xMove < 0;
		if (left) {
			content[y][3] = content[y][0];
			content[y][3].onMoveDontChangePiece(content, 0, y, 3, y);
			content[y][0] = null;
		} else {
			content[y][BOARD_SIZE - 1 - 2] = content[y][BOARD_SIZE - 1];
			content[y][BOARD_SIZE - 1 - 2].onMoveDontChangePiece(content,
					BOARD_SIZE - 1 - 2, y, BOARD_SIZE - 1, y);
			content[y][BOARD_SIZE - 1] = null;
		}
		return true;
	}

	/**
	 * Checks, whether the king is in check
	 * 
	 * @return <b>true</b> if king is in check
	 */
	public boolean isInCheck(Piece[][] content, int x, int y) {
		// check diagonally for queen or bishop
		if (checkDiagonally(content, x, y)) {
			return true;
		}

		// check along rank and file for a rook and queen
		if (checkAlongRankAndFile(content, x, y)) {
			return true;
		}

		// check for knight
		if (checkForKnight(content, x, y)) {
			return true;
		}

		// check for pawn
		if (checkForPawn(content, x, y)) {
			return true;
		}

		// check for king
		if (checkForKing(content, x, y)) {
			return true;
		}

		return false;
	}

	private boolean checkForKing(Piece[][] content, int x, int y) {
		for (int xTry = x - 1; xTry <= x + 1; xTry++) {
			for (int yTry = y - 1; yTry <= y + 1; yTry++) {
				if ((xTry != x || yTry != x)
						&& isSquareOnBoard(content, xTry, yTry)) {
					Piece pieceCheck = content[yTry][xTry];
					if (pieceCheck != null && pieceCheck.getFraction() != fraction
							&& pieceCheck.getType() == Type.KING) {
						return true;
					}
				}
			}
		}
		return false;
	}

	private boolean checkForPawn(Piece[][] content, int x, int y) {
		int yDirection = fraction == Fraction.WHITE ? -1 : 1;
		for (int xTry = x - 1, yTry = y + yDirection, i = 0; i < 2; xTry = x + 1, i++) {
			if (isSquareOnBoard(content, xTry, yTry)) {
				Piece pieceCheck = content[yTry][xTry];
				if (pieceCheck != null && pieceCheck.getFraction() != fraction
						&& pieceCheck.getType() == Type.PAWN) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean checkForKnight(Piece[][] content, int x, int y) {
		for (int xMove = 1, yMove = 2, i = 0; i < 2; xMove = 2, yMove = 1, i++) {
			for (int xTry = x - xMove, j = 0; j < 2; xTry = x + xMove, j++) {
				for (int yTry = y - yMove, k = 0; k < 2; yTry = y + yMove, k++) {
					if (isSquareOnBoard(content, xTry, yTry)) {
						Piece pieceCheck = content[yTry][xTry];
						if (pieceCheck != null
								&& pieceCheck.getFraction() != fraction
								&& pieceCheck.getType() == Type.KNIGHT) {
							return true;
						}
					}
				}
			}
		}
		return false;
	}

	private boolean checkAlongRankAndFile(Piece[][] content, int x, int y) {
		for (int moveX = 0, moveY = 1, i = 0; i < 2; moveX = 1, moveY = 0, i++) {
			for (int xDirection = -moveX, yDirection = -moveY, j = 0; j < 2; xDirection = moveX, yDirection = moveY, j++) { // 4
																															// directions

				for (int xTry = x + xDirection, yTry = y + yDirection; isSquareOnBoard(
						content, xTry, yTry); xTry += xDirection, yTry += yDirection) {
					if (xTry != x || yTry != y) {
						Piece pieceCheck = content[yTry][xTry];
						if (pieceCheck != null) {
							if (pieceCheck.getFraction() != fraction) {
								if (pieceCheck.getType() == Type.ROOK
										|| pieceCheck.getType() == Type.QUEEN) {
									return true;
								}
							}
							break;
						}
					}
				}

			}
		}
		return false;
	}

	private boolean checkDiagonally(Piece[][] content, int x, int y) {
		for (int xDirection = -1, i = 0; i < 2; xDirection = 1, i++) {
			for (int yDirection = -1, j = 0; j < 2; yDirection = 1, j++) { // 4
																			// directions

				for (int xTry = x + xDirection, yTry = y + yDirection; isSquareOnBoard(
						content, xTry, yTry); xTry += xDirection, yTry += yDirection) { // tries
																						// 1
																						// direction
					if (xTry != x || yTry != y) {
						Piece pieceCheck = content[yTry][xTry];
						if (pieceCheck != null) {
							if (pieceCheck.getFraction() != fraction) {
								if (pieceCheck.getType() == Type.BISHOP
										|| pieceCheck.getType() == Type.QUEEN) {
									return true;
								}
							}
							break;
						}
					}
				}

			}
		}
		return false;
	}
}
